<?php


namespace App\Web\Model;

use App\Exception\RightChangeException;
use App\Facades\Captcha;
use App\Service\Sms as SmsService;
use App\Web\Model\Redis\LoginSession;
use App\Web\Model\User as UserModel;
use EchoPHP\Facades\Log;
use EchoPHP\Facades\Redis;
use EchoPHP\Facades\Request;
use Ramsey\Uuid\Uuid;

class Login extends Model {

    const LOGIN_INT_NET = '01';
    /**
     * 公网登录
     */
    const LOGIN_PUB_NET = '02';

    /**
     * @var LoginSession
     */
    protected $loginSessionHandler;

    public function __construct() {
        $this->loginSessionHandler = new LoginSession();
    }

    /**
     * 用户登录
     * @param $userName 用户名
     * @param $password 密码
     * @return array
     * @throws \Exception
     */
    public function login($userName, $password) {
        if (is_null($userName) || empty($password)) {
            return [
                'status' => -1,
                'message' => '用户名或密码不正确',
            ];
        }

        $userModel = new UserModel();
        $userInfo = $userModel->getInfoWithRoleByUsername($userName);

        if (empty($userInfo) || $userInfo['password'] != $userModel->encryptPwd($userInfo['username'], $password)) {
            return [
                'status' => -1,
                'message' => '用户名或密码不正确',
            ];
        }
        if ($userInfo['islock'] != '0') {
            return [
                'status' => -2,
                'message' => '该账号已被锁定',
            ];
        }
        //内网不强制手机号登陆 192.168.0.0/16  10.0.0.0/8
        $clientIp = Request::instance()->getClientIp();
        $ipArr = explode('.', $clientIp);
        $mobileLogin =  $ipArr[0] . $ipArr[1] == '192168' || $ipArr[0] == '10';
        if ($userInfo['mobileauth'] == 1 && env('FORCE_MP_LOGIN', true) && !$mobileLogin) {
            return [
                'status' => -4,
                'message' => '账号已绑定手机，请用手机号进行登录',
            ];
        }

        return $this->generateToken($userInfo);
    }

    /**
     * 手机登录
     * @param $telPhone
     * @param $verifyCode
     * @return array
     * @throws \Exception
     */
    public function loginByPhone($telPhone, $verifyCode) {
        if (is_null($telPhone) || empty($verifyCode)) {
            return [
                'status' => -1,
                'message' => '手机号和验证码不能为空',
            ];
        }
        $userModel = new UserModel();
        $userInfo = $userModel->getInfoWithRoleByPhone($telPhone);
        if (empty($userInfo)) {
            return [
                'status' => -2,
                'message' => '手机号不正确',
            ];
        }
        if ($userInfo['islock'] != '0') {
            return [
                'status' => -3,
                'message' => '该账号已被锁定',
            ];
        }
        //校验短信验证码
        $result = Captcha::check('phone', $userInfo['userid'] . '_login', $verifyCode);
        if (!$result) {
            return [
                'status' => -4,
                'message' => '验证码错误',
                'errorFields' => ['phoneverify' => '手机验证码错误']
            ];
        }

        return $this->generateToken($userInfo);
    }

    /**
     * 生成token
     * @param $userInfo
     * @return array
     * @throws \Exception
     */
    protected function generateToken($userInfo) {
        //判断是否有其他位置登录
        $refreshToken = $this->loginSessionHandler->generateRefreshToken($userInfo['userid'], $userInfo['username']);
        if ($this->loginSessionHandler->hasSession($userInfo['userid'])) {
            return [
                'status' => -5,
                'message' => '其他终端已登录，是否将其强制下线',
                'refreshToken' => $refreshToken,
            ];
        }
        $authToken = $this->loginSessionHandler->setSession($userInfo);
        if (false === $authToken) {
            return [
                'status' => -6,
                'message' => '登录失败',
            ];
        }
        //更新最后登录时间
        $this->updateLoginDate($userInfo['username']);
        return [
            'status' => 0,
            'message' => '登录成功',
            'authtoken' => $authToken
        ];
    }

    /**
     * 强制下线并登录
     * @param $refreshToken
     * @return array
     * @throws \Exception
     */
    public function replaceLogin($refreshToken) {
        $authToken = $this->setLoginInfo($refreshToken);
        if (false === $authToken) {
            return [
                'status' => false,
                'message' => '登录失败',
            ];
        }
        $this->updateLoginDate($this->loginSessionHandler->chkRefreshToken($refreshToken)['username']);
        return [
            'status' => true,
            'message' => '登录成功',
            'authtoken' => $authToken
        ];
    }

    /**
     * 根据兑换token设置登录会话
     * @param $refreshToken
     * @return bool|int
     * @throws \Exception
     */
    protected function setLoginInfo($refreshToken) {
        if (false === $loginInfo = $this->loginSessionHandler->chkRefreshToken($refreshToken)) {
            return false;
        }
        $userModel = new UserModel();
        $userInfo = $userModel->getInfoWithRole($loginInfo['userid']);
        if (empty($userInfo)) {
            return false;
        }
        if (false === $authToken = $this->loginSessionHandler->setSession($userInfo)) {
            return false;
        }
        return $authToken;
    }

    /**
     * 用户注销
     * @param $userId
     */
    public function logout($userId) {
        $this->loginSessionHandler->clearSession($userId);
    }

    /**
     * 强制下线
     * @param $oprId
     * @param $userId
     * @param null $message
     * @throws \Exception
     */
    public function excluded($oprId, $userId, $message = null) {
        $userInfo = $this->loginSessionHandler->clearSession($userId);
        if (isset($userInfo['token']) && !empty($message)) {
            $this->loginSessionHandler->sessionMsg($userInfo['token'], $message);
        }
    }

    /**
     * 验证身份token是否正确
     * @param $authToken
     * @return array|bool
     * @throws \Exception
     */
    public function check($authToken) {
        $loginInfo = $this->loginSessionHandler->chkSession($authToken);
        //todo- 为解决不用用户共享相同的authtoken来规避单点登录的问题，authtoken会动态变化，但需要返回最新的authtoken，暂不处理
        if (time() - $loginInfo['createat'] > 300) {
            if (false === ($loginInfo = $this->resetLoginInfo($loginInfo))) {
                return false;
            }
            try {
                $loginInfo = $this->loginSessionHandler->chkSession($authToken);
            } catch (RightChangeException $e) {
                $this->excluded(0, $loginInfo['userid'], '您的账号权限已变化，请重新登录');
                throw $e;
            }

        }
        return $loginInfo;
    }

    /**
     * 重置登录会话信息
     * @param $loginInfo
     * @return bool|array
     * @throws \Exception
     */
    protected function resetLoginInfo($loginInfo) {
        $userModel = new UserModel();
        $userInfo = $userModel->getInfoWithRole($loginInfo['userid']);
        if (empty($userInfo)) {
            return false;
        }
        if (false === ($loginInfo = $this->loginSessionHandler->refreshSession($userInfo, $loginInfo['token']))) {
            return false;
        }
        return $loginInfo;
    }

    /**
     * 获取单个用户的登录状态
     * @param $userId
     * @return bool
     */
    public function getLoginStatus($userId) {
        return $this->loginSessionHandler->hasSession($userId);
    }

    /**
     * 判断用户是否在线
     * @param array $userIds
     * @return array
     */
    public function getMultiLoginStatus(array $userIds) {
        $loginStatus = [];
        foreach ($userIds as $userId) {
            $loginStatus[$userId] = $this->getLoginStatus($userId);
        }
        return $loginStatus;
    }

    /**
     * 更新最后登录时间
     * @param $userName
     * @return int
     */
    public function updateLoginDate($userName) {
        $redisClient = Redis::connect('userinfo');
        $keyName = 'userinfo:' . $userName;
        $result = $redisClient->hSet($keyName, 'logindate', date('Y-m-d H:i:s'));
        //hSet 返回三个值，详见hSet文档，要排除掉0的情况。
        if ($result === false) {
            Log::error('用户' . $userName . '登录，redis更新最后登录时间失败，File:' . __FILE__ . ',Line:' . __LINE__);
        }
        return 0;
    }

    /**
     * 发送短信登陆验证码
     * @param $uuid
     * @param $captchaCode
     * @param $mobilePhone
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function sendMpLoginCode($uuid, $captchaCode, $mobilePhone) {
        $action = '_login';
        //图形验证码校验
        $result = Captcha::check('image', ($uuid . $action), $captchaCode);

        if (!$result) {
            return ['status' => -1, 'message' => '验证码错误,请重新输入'];
        }

        //获取用户信息并保存验证码
        $userModel = new UserModel();
        $userInfo = $userModel->getInfoWithRoleByPhone($mobilePhone);
        if (empty($userInfo)) {
            return ['status' => -2, 'message' => '请确认手机号'];
        }
        $userId = $userInfo['userid'];
        $verifyCode = Captcha::generate('phone', $userId . $action);
        if ($verifyCode['error'] != 0) {
            return ['status' => -10, 'message' => '发送验证码出现错误', 'error' => $verifyCode];
        }
        //发送验证码
        $sms = new SmsService();
        $channel = config('app.sms.channel.account');
        $content = '欢迎登陆无忧RPO系统，登陆验证码为' . $verifyCode['code'] . ',有效时间' . floor($verifyCode['expire'] / 60) . '分钟。请勿泄露给他人';
        $result = $sms->send($userInfo['mobilephone'], $content, $channel);
        if ($result) {
            return ['status' => 0, 'message' => '短信发送成功'];
        }
        return ['status' => -3, 'message' => '短信发送失败'];
    }

}